package com.gmsk.pocket.service.impl;

import com.gmsk.pocket.dao.DocMapper;
import com.gmsk.pocket.entity.Doc;
import com.gmsk.pocket.service.DocService;
import com.gmsk.pocket.service.ThreadPoolExecutorService;
import com.hankcs.hanlp.HanLP;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@Service
public class DocServiceImpl implements DocService {

    @Autowired
    DocMapper docMapper;

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public int insert(Doc doc) {
        return docMapper.insert(doc);
    }

    @Override
    public int update(Doc doc) {
        return docMapper.updateByPrimaryKey(doc);
    }

    @Override
    public int delete(Integer id) {
        return docMapper.deleteByPrimaryKey(id);
    }

    @Override
    public Doc select(Integer id) {
        return docMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Doc> selectByType(Integer typeId, Integer start, Integer limit) {
        return docMapper.selectByType(typeId, start, limit);
    }

    @Override
    public Integer selectCount(Integer typeId) {
        return docMapper.selectCount(typeId);
    }

   /* public Result searchDoc(Map<String, Object> params, int page, int size){
        Long dateStart =(Long) params.get("dateStart");
        Long dateEnd =(Long) params.get("dateEnd");
        String docType =(String) params.get("docType");
        //文档大小
        //0表示不限，1表示0Kb-128kb,2表示128kb-512kb,
        //3表示512kb-1mb,4表示1mb-5mb,5表示5mb以上
        Integer docSizeLevel =Integer.valueOf(params.get("docSizeLevel").toString());
        String words  =(String) params.get("words");
        //搜索词位置 0表示所有位置，1表示在文档内容，2表示在文档标题
        Integer wordsLocation = Integer.valueOf(params.get("wordsLocation").toString());
        NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();

        BoolQueryBuilder query = QueryBuilders.boolQuery();
        BoolQueryBuilder filterQuery = QueryBuilders.boolQuery();

        List<QueryBuilder> filterCnd = filterQuery.must();
        if(dateStart!=null&&dateEnd!=null){
            filterCnd.add(QueryBuilders.rangeQuery("docModifyDate").gte(dateStart).lt(dateEnd));
        }
        if(docType!=null&&!docType.equals("all")){
            BoolQueryBuilder docTypeQuery = QueryBuilders.boolQuery();
            filterCnd.add(docTypeQuery);
            List<QueryBuilder> docTypeCnd = docTypeQuery.should();
            if(docType.equals("pdf")){
                docTypeCnd.add(QueryBuilders.termQuery("docType","pdf"));
            }
            if(docType.equals("powerpoint")){
                docTypeCnd.add(QueryBuilders.termQuery("docType","ppt"));
                docTypeCnd.add(QueryBuilders.termQuery("docType","pptx"));
            }
            if(docType.equals("word")){
                docTypeCnd.add(QueryBuilders.termQuery("docType","doc"));
                docTypeCnd.add(QueryBuilders.termQuery("docType","docx"));
            }
            if(docType.equals("excel")){
                docTypeCnd.add(QueryBuilders.termQuery("docType","xls"));
                docTypeCnd.add(QueryBuilders.termQuery("docType","xlsx"));
            }
            if(docType.equals("html")){
                docTypeCnd.add(QueryBuilders.termQuery("docType","html"));
            }
        }

        if(docSizeLevel!=null){
            Long sizeStart= null;
            Long sizeEnd = null;
            switch (docSizeLevel){
                case 1:
                    sizeStart = 0L;
                    sizeEnd = 1024*128L;
                    break;
                case 2:
                    sizeStart = 1024*128L;
                    sizeEnd = 1024*512L;
                    break;
                case 3:
                    sizeStart = 1024*512L;
                    sizeEnd = 1024*1024L;
                    break;
                case 4:
                    sizeStart = 1024*1024L;
                    sizeEnd = 1024*1024*5L;
                    break;
                case 5:
                    sizeStart = 1024*1024*5L;
                    break;
            }
            RangeQueryBuilder docSizeRange = null;
            if(sizeStart!=null){
                docSizeRange = QueryBuilders.rangeQuery("docSize");
                docSizeRange.from(sizeStart);
            }
            if(sizeEnd!=null){
                docSizeRange.to(sizeEnd);
            }
            if(docSizeRange!=null){
                filterCnd.add(docSizeRange);
            }
        }
        filterCnd.add(QueryBuilders.termQuery("docDelete",0));
        if(wordsLocation!=null){
            MultiMatchQueryBuilder search=null;
            if(wordsLocation==0){
                search = QueryBuilders.multiMatchQuery(words,"docTitle","docContent");
            }else if(wordsLocation==1){
                search = QueryBuilders.multiMatchQuery(words,"docContent");
            }else if(wordsLocation==2){
                search = QueryBuilders.multiMatchQuery(words,"docTitle");
            }
            if(search!=null){
                query.must(search.type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                        //.analyzer("ik_max_word")
                        .maxExpansions(10));
            }
        }else {
            query.must(QueryBuilders.multiMatchQuery(words,"docContent","docTitle").type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                    //.analyzer("ik_max_word")
                    .maxExpansions(10));
        }

        query.filter(filterQuery);
        searchQuery.withQuery(query);
        searchQuery.withHighlightFields(
                new HighlightBuilder.Field("docContent").fragmentSize(50).numOfFragments(2),//.highlighterType("fvh").fragmentSize(200).numOfFragments(10),
                new HighlightBuilder.Field("docTitle")//.highlighterType("fvh")
        );
        NativeSearchQuery nativeSearchQuery = searchQuery.build();
        logger.info("query cnd:{}",nativeSearchQuery.getQuery());
        nativeSearchQuery.addSourceFilter(new FetchSourceFilter(new String[]{"docSize","docTitle","docType","docCreateDate","docId"},null));
        Pageable pageable = PageRequest.of(page,size);
        nativeSearchQuery.setPageable(pageable);
        Page<Doc> docPage = elasticsearchTemplate.queryForPage(nativeSearchQuery, Doc.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                List<Doc> chunk = new ArrayList<>();
                for (SearchHit searchHit : response.getHits()) {
                    if (response.getHits().getHits().length <= 0) {
                        return null;
                    }
                    logger.info("score:{}",searchHit.getScore());
                    chunk.add(searchHitToDoc(searchHit));
                }
                if (chunk.size() > 0) {
                    return new AggregatedPageImpl<>((List<T>) chunk);
                }
                return null;
            }
        });
        if(docPage==null){
            return new Result(null, 0L);
        }
        List<Doc>docList =  StreamSupport.stream(docPage.spliterator(),false).collect(Collectors.toList());
        completeDoc(docList);
        Long total = elasticsearchTemplate.count(nativeSearchQuery);
        return new Result(docList, total);
    }


    private Doc searchHitToDoc(SearchHit searchHit) {
        Doc doc;
        try {
            doc = defaultEntityMapper.mapToObject(searchHit.getSourceAsString(), Doc.class);
        } catch (IOException e) {
            throw new ElasticsearchException("failed to map source [ " + searchHit.getSourceAsString() + "] to class " + Doc.class.getSimpleName(), e);
        }
        HighlightField docContentHighlightField=searchHit.getHighlightFields().get("docContent");
        HighlightField docTitleHighlightField =searchHit.getHighlightFields().get("docTitle");
        if(docContentHighlightField!=null){
            List<String> docContentHighLight = Arrays.stream(docContentHighlightField.fragments()).map(Text::string).collect(Collectors.toList());
            StringBuilder sb =new StringBuilder();
            docContentHighLight.stream().forEach(sb::append);
            doc.setDocContent(sb.toString());
        }
        if(docTitleHighlightField!=null){
            List<String> docTitleHighLight = Arrays.stream(docTitleHighlightField.fragments()).map(Text::string).collect(Collectors.toList());
            StringBuilder sb =new StringBuilder();
            docTitleHighLight.stream().forEach(sb::append);
            doc.setDocTitle(sb.toString());
        }
        return doc;
    }

    public class Result {
        List<Doc> docList;
        Long size;

        public Result(List<Doc> docList, Long size) {
            this.docList = docList;
            this.size = size;
        }

        public List<Doc> getDocList() {
            return docList;
        }

        public void setDocList(List<Doc> docList) {
            this.docList = docList;
        }

        public Long getSize() {
            return size;
        }

        public void setSize(Long size) {
            this.size = size;
        }
    }


    private void completeDoc(List<Doc> docList) {
        List<Long> docIdList = docList.stream().map(Doc::getDocId).collect(Collectors.toList());
        Map<Long, Doc> map = new HashMap<>();
        List<Doc> docs = docRepository.findAllByDocIdIn(docIdList);
        docs.forEach(doc -> {
            map.put(doc.getDocId(), doc);
        });
        docList.forEach(doc ->  doc.setDocConvert(map.get(doc.getDocId()).getDocConvert()));
    }*/


}
